#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <fstream>
#include "global.h"
#include "ltimer.h"

/* 
sdl2 可以根据时间设置运动，使其不受帧频的影响
*/

//全部面积大小
// 由于渲染的大小不再是屏幕的大小，必须有一组单独的常量来定义渲染的大小。
const int LEVEL_WIDTH = 1280;
const int LEVEL_HEIGHT = 960;

//当前框大小
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

TTF_Font* gFont = nullptr; 
// 渲染的窗口
SDL_Window * gWindow = nullptr;
//The window renderer
SDL_Renderer* gRenderer = nullptr;

// 封装SDL_Texture
//Texture wrapper class
class LTexture
{
    public:
        //Initializes variables
        LTexture();

        //Deallocates memory
        ~LTexture();

        //Loads image at specified path
        bool loadFromFile( std::string path );

        #if defined(SDL_TTF_MAJOR_VERSION)
        /*
        如果头文件不包含 SDL_ttf.h，编译器就会忽略它。它检查是否定义了 SDL_TTF_MAJOR_VERSION 宏。
        与 #include 一样，#if 也是用于与编译器对话的宏。在本例中，它表示如果 SDL_ttf 未定义，则忽略这段代码
        */
        //Creates image from font string
        // SDL_ttf 的工作方式是根据字体和颜色创建新图像。对于纹理类来说，这意味着将从 SDL_ttf 渲染的文本而不是文件中加载图像
        bool loadFromRenderedText( std::string textureText, SDL_Color textColor );
        #endif

        //Loads image into pixel buffer  加载像素 
        bool loadPixelsFromFile( std::string path );

        //Creates image from preloaded pixels  将纹理转化为像素  
        bool loadFromPixels();

        //createBlank 函数分配了一个空白纹理，可以在流式传输时将数据复制到该纹理中
        bool createBlank( int width, int height );

        //接收了access参数，该参数定义了如何访问它
        bool createBlank( int width, int height, SDL_TextureAccess access );

        //Deallocates texture
        void free();

        //设置调制颜色，接收红、绿、蓝三色分量
        void setColor( Uint8 red, Uint8 green, Uint8 blue );

        //设置纹理混合  它将控制纹理的混合方式。为了使混合正常工作，必须设置纹理的混合模式
        void setBlendMode( SDL_BlendMode blending );

        //设置纹理的 alpha 透明度
        void setAlpha( Uint8 alpha );

        //Renders texture at given point
        /*接受一个矩形参数，用来定义我们要渲染的纹理部分。我们给它一个默认参数 nullptr，以防我们想要渲染整个纹理
          接受旋转角度、纹理旋转点和 SDL 翻转枚举值，同样也给出了参数的默认值，以备在不旋转或翻转的情况下渲染纹理。
        */
        void render( int x, int y, SDL_Rect* clip = nullptr, double angle = 0.0, SDL_Point* center = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE );

        //渲染该纹理
        void setAsRenderTarget();

        //Gets image dimensions
        int getWidth();
        int getHeight();

        //Pixel accessors
        /*
        间距基本上就是纹理在内存中的宽度。在一些老式硬件和移动硬件上，纹理的大小会受到限制。
        如果创建一个宽度为 100 像素的纹理，它可能会被填充为 128 像素宽（2 的下一次幂）。
        通过间距，可以知道图像在内存中的位置
        */
        Uint32* getPixels32();  // 获取原始像素
        Uint32 getPitch32();   // 获取像素间距
        Uint32 getPixel32( Uint32 x, Uint32 y );  // 获取精确 x/y 坐标的像素
        // copyRawPixels()函数会复制要流式传输的原始像素数据
        void copyRawPixels32( void* pixels );
        //  lockTexture()，可以获取一个指针来发送像素
        bool lockTexture();
        // unlockTexture()则可以将像素数据上传到像素
        bool unlockTexture();
 
    private:
        //实际渲染的纹理
        SDL_Texture* mTexture;

        //Surface pixels
        SDL_Surface* mSurfacePixels;

        //原始像素
        void* mRawPixels;
        int mRawPitch;

        //图像的宽和高
        int mWidth;
        int mHeight;
};

LTexture::LTexture()
{
    //Initialize
    mTexture = nullptr;
    mWidth = 0;
    mHeight = 0;
}

LTexture::~LTexture()
{
    //Deallocate
    free();
}

void LTexture::setColor( Uint8 red, Uint8 green, Uint8 blue )
{
    //纹理颜色调制
    /*
    DL_SetTextureColorMod 接受 Uint8 作为颜色组件的参数。Uint8 只是一个无符号的 8 位整数。这意味着它的范围从 0 到 255。
    128 大约介于 0 和 255 之间，因此当您将绿色调制为 128 时，纹理上任何像素的绿色分量都会减半。
    红色和蓝色方格不会受到影响，因为它们没有绿色，但绿色的亮度会减半，白色则会变成浅洋红色（洋红色为红色 255、绿色 0、蓝色 255）。
    颜色调制只是将一种颜色乘以整个纹理的一种方法
    */
    SDL_SetTextureColorMod( mTexture, red, green, blue );
}

#if defined(SDL_TTF_MAJOR_VERSION)
bool LTexture::loadFromRenderedText( std::string textureText, SDL_Color textColor )
{
    ////先取消原有纹理的渲染
    free();

    //Render text surface
    /*
    根据字体创建要渲染的纹理。该函数接收要渲染的文本字符串和要用来渲染它的颜色。在此之后，该函数的工作原理与从文件加载相似，
    只不过这次使用的是由 SDL_ttf 创建的 SDL_Surface，而不是文件
    使用 TTF_RenderText_Solid 加载一个曲面。函数会根据给出的字体、文本和颜色创建一个纯色曲面。如果曲面创建成功，
    就会像之前从文件加载曲面一样创建纹理。创建纹理后，我们就可以像渲染其他纹理一样对其进行渲染。
    */
    if(gFont == nullptr)
    {
        std::cout << "gFont nullptr" << std::endl;
    }
    SDL_Surface* textSurface = TTF_RenderText_Solid( gFont, textureText.c_str(), textColor );
    if( textSurface == nullptr )
    {
        std::cout << "Unable to render text surface! SDL_ttf Error:" << TTF_GetError() << std::endl;
    }
    else
    {
        //Create texture from surface pixels
        mTexture = SDL_CreateTextureFromSurface( gRenderer, textSurface );
        if( mTexture == nullptr )
        {
            std::cout << "Unable to create texture from rendered text! SDL Error:" << SDL_GetError() << std::endl;
        }
        else
        {
            //Get image dimensions
            mWidth = textSurface->w;
            mHeight = textSurface->h;
        }

        //Get rid of old surface
        SDL_FreeSurface( textSurface );
    }
    
    //Return success
    return mTexture != NULL;
}
#endif

bool LTexture::loadPixelsFromFile( std::string path )
{
    //Free preexisting assets
    // free();

    //Load image at specified path
    SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
    if( loadedSurface == nullptr )
    {
        std::cout << "Unable to load image" << path.c_str() << "! SDL_image Error:" << IMG_GetError() << std::endl;
    }
    else
    {
        //将现有的表面复制到指定格式的新的表面 第一个参数为源表面指针，第二个参数为像素格式，第三个参数设置为0即可
        mSurfacePixels = SDL_ConvertSurfaceFormat( loadedSurface, SDL_GetWindowPixelFormat( gWindow ), 0 );
        if( mSurfacePixels == nullptr )
        {
            std::cout << "Unable to convert loaded surface to display format! SDL Error:" << IMG_GetError() << std::endl;
        }
        else
        {
            //Get image dimensions
            mWidth = mSurfacePixels->w;
            mHeight = mSurfacePixels->h;
        }

        //Get rid of old loaded surface
        SDL_FreeSurface( loadedSurface );
    }

    return mSurfacePixels != nullptr;
}

// 加载纹理
bool LTexture::loadFromPixels()
{
    //Only load if pixels exist
    if( mSurfacePixels == nullptr )
    {
        std::cout << "No pixels loaded!" << std::endl;
    }
    else
    {
        //Color key image
        SDL_SetColorKey( mSurfacePixels, SDL_TRUE, SDL_MapRGB( mSurfacePixels->format, 0, 0xFF, 0xFF ) );

        //Create texture from surface pixels
        mTexture = SDL_CreateTextureFromSurface( gRenderer, mSurfacePixels );
        if( mTexture == nullptr )
        {
            std::cout << "Unable to create texture from loaded pixels! SDL Error:" << SDL_GetError() << std::endl;
        }
        else
        {
            //Get image dimensions
            mWidth = mSurfacePixels->w;
            mHeight = mSurfacePixels->h;
        }

        //Get rid of old loaded surface
        SDL_FreeSurface( mSurfacePixels );
        mSurfacePixels = nullptr;
    }

    //Return success
    return mTexture != nullptr;
}

bool LTexture::loadFromFile( std::string path )
{
    //先取消原有纹理的渲染
    // free();

    //Load pixels
    if( !loadPixelsFromFile( path ) )
    {
        std::cout<< "Failed to load pixels for " << path.c_str() << std::endl;
    }
    else
    {
        //Load texture from pixels
        if( !loadFromPixels() )
        {
            std::cout<< "Failed to texture from pixels from " << path.c_str() << std::endl;
        }
    }
    return mTexture != nullptr;
}

Uint32* LTexture::getPixels32()
{
    Uint32* pixels = nullptr;

    if( mSurfacePixels != nullptr )
    {
        // pixels =  static_cast( mSurfacePixels->pixels );
        pixels =  static_cast<Uint32*>( mSurfacePixels->pixels );
    }

    return pixels;
}

Uint32 LTexture::getPitch32()
{
    Uint32 pitch = 0;

    if( mSurfacePixels != nullptr )
    {
        // 间距是以字节表示的，需要的是以像素为单位的间距，而每个像素有 32 位/4 字节，因此可以通过除以每个像素的 4 字节来得到每个间距的像素数
        pitch = mSurfacePixels->pitch / 4;
    }

    return pitch;
}

void LTexture::free()
{
    //Free texture if it exists
    if( mTexture != nullptr )
    {
        SDL_DestroyTexture( mTexture );
        mTexture = nullptr;
        mWidth = 0;
        mHeight = 0;
    }

    //Free surface if it exists
    if( mSurfacePixels != nullptr )
    {
        SDL_FreeSurface( mSurfacePixels );
        mSurfacePixels = nullptr;
    }
}

void LTexture::render( int x, int y, SDL_Rect* clip, double angle, SDL_Point* center, SDL_RendererFlip flip )
{
    //设置渲染区域并渲染到屏幕
    SDL_Rect renderQuad = { x, y, mWidth, mHeight };
    /* 在特定位置渲染纹理时，需要指定一个目标矩形，该矩形可设置 x/y 位置和宽度/高度。在不知道原始图像尺寸的情况下，我们无法指定宽度/高度。
    因此，当我们渲染纹理时，我们会创建一个包含位置参数和成员宽度/高度的矩形，并将此矩形传递给 SDL_RenderCopy
    */

    //Set clip rendering dimensions
    if( clip != nullptr )
    {
        renderQuad.w = clip->w;
        renderQuad.h = clip->h;
    }
    /*
    在剪辑时，如果使用的是剪辑矩形的尺寸而不是纹理的尺寸，我们将把目标矩形（此处称为 renderQuad）的宽度/高度设置为剪辑矩形的尺寸。
    我们要将剪辑矩形作为源矩形传递给 SDL_RenderCopy。源矩形定义了要渲染纹理的哪一部分。当源矩形为空时，将渲染整个纹理。
    */

    //Render to screen
    // SDL_RenderCopy( gRenderer, mTexture, clip, &renderQuad );
    //Render to screen 该函数的工作原理与原始的 SDL_RenderCopy 相同，但增加了用于旋转和翻转的参数
    SDL_RenderCopyEx( gRenderer, mTexture, clip, &renderQuad, angle, center, flip );
}

int LTexture::getWidth()
{
    return mWidth;
}

int LTexture::getHeight()
{
    return mHeight;
}

void LTexture::setBlendMode( SDL_BlendMode blending )
{
    //Set blending function
    // SDL_SetTextureBlendMode 允许启用混合模式
    SDL_SetTextureBlendMode( mTexture, blending );
}
        
void LTexture::setAlpha( Uint8 alpha )
{
    //Modulate texture alpha
    // SDL_SetTextureAlphaMod 则允许设置整个纹理的 Alpha 值
    SDL_SetTextureAlphaMod( mTexture, alpha );
}

// 获取特定位置像素
Uint32 LTexture::getPixel32( Uint32 x, Uint32 y )
{
    //Convert the pixels to 32 bit
    Uint32* pixels = static_cast<Uint32*>( mSurfacePixels->pixels );

    //一个二维纹理图像 像素是以一个维度存储的
    return pixels[ ( y * getPitch32() ) + x ];
}

//创建一个 32 位 RGBA 纹理并进行流访问。创建纹理时必须确保的一点是，纹理像素的格式要与流式传输的像素格式一致
bool LTexture::createBlank( int width, int height )
{
    //Get rid of preexisting texture
    free();

    //Create uninitialized texture
    mTexture = SDL_CreateTexture( gRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height );
    if( mTexture == nullptr )
    {
        std::cout << "Unable to create streamable blank texture! SDL Error: " << SDL_GetError() << std::endl;
    }
    else
    {
        mWidth = width;
        mHeight = height;
    }

    return mTexture != nullptr;
}

bool LTexture::createBlank( int width, int height, SDL_TextureAccess access )
{
    //Get rid of preexisting texture
    free();

    //Create uninitialized texture
    mTexture = SDL_CreateTexture( gRenderer, SDL_PIXELFORMAT_RGBA8888, access, width, height );
    if( mTexture == NULL )
    {
        std::cout << "Unable to create streamable blank texture! SDL Error: " << SDL_GetError() << std::endl;
    }
    else
    {
        mWidth = width;
        mHeight = height;
    }

    return mTexture != NULL;
}

// 对纹理进行渲染，将其设置为渲染目标
void LTexture::setAsRenderTarget()
{
    //Make self render target
    SDL_SetRenderTarget( gRenderer, mTexture );
}

/*
SDL_LockTexture() 会抓取一个像素数据指针和纹理的间距。注意：该指针不会包含原始纹理的数据。如果创建了一个纹理，然后将其锁定，则不要指望它拥有您创建纹理时使用的原始像素数据。
用SDL_LockTexture()提供一个新的像素指针，以便向纹理发送新数据。SDL_LockTexture() 还会以字节为单位抓取纹理间距。
*/
bool LTexture::lockTexture()
{
    bool success = true;

    //Texture is already locked
    if( mRawPixels != nullptr )
    {
        std::cout << "Texture is already locked!" << std::endl;
        success = false;
    }
    //Lock texture
    else
    {   
        // 函数功能：锁定纹理，第一个参数 纹理指针。第二个参数 锁定区域，NULL 为整个纹理，第三个参数 像素，第四个参数 程度

        if( SDL_LockTexture( mTexture, nullptr, &mRawPixels, &mRawPitch ) != 0 )
        {
            std::cout << "Unable to lock texture! " << SDL_GetError() << std::endl;
            success = false;
        }
    }

    return success;
}

// 用所需的数据完成了对像素指针的操作，解锁它就会将像素数据发送到 GPU。
bool LTexture::unlockTexture()
{
    bool success = true;

    //Texture is not locked
    if( mRawPixels == nullptr )
    {
        std::cout << "Texture is not locked!" << std::endl;
        success = false;
    }
    //Unlock texture
    else
    {
        SDL_UnlockTexture( mTexture );
        mRawPixels = nullptr;
        mRawPitch = 0;
    }

    return success;
}

// 用于将数据流中的像素复制到锁定的纹理像素指针中。该函数假定像素来自与纹理大小相同的图像
void LTexture::copyRawPixels32( void* pixels )
{
    //Texture is locked
    if( mRawPixels != nullptr )
    {
        //Copy to locked pixels
        memcpy( mRawPixels, pixels, mRawPitch * mHeight );
    }
}

LTexture gTargetTexture;
LTexture gDotTexture;

//Render text r,g,b,a
SDL_Color textColor = { 0, 0xff, 0xff, 0xff };

//点类，可以在屏幕上移动
class Dot
{
    public:
        //点的尺寸
        static const int DOT_WIDTH = 20;
        static const int DOT_HEIGHT = 20;

        //点的最大轴速度  在每秒 60 帧的应用中，每秒 640 像素相当于每帧 10 像素多一点
        static const int DOT_VEL = 640;

        //Initializes the variables
        Dot(int x, int y);

        Dot();

        ~Dot();

        //按下按键并调整点的速度
        void handleEvent( SDL_Event& e );

        //移动点并检查碰撞 移动函数将接收一个碰撞方框组，这样就可以对两组碰撞方框进行检查
        void move(std::vector<SDL_Rect>& otherColliders);

        //Moves the dot and checks collision
        void move( SDL_Rect& square, Circle& circle );

        void move();

        //Moves the dot 为了根据帧时间移动，移动函数需要知道每帧移动的时间。移动功能需要一个时间步长，也就是自上次更新后过去了多少时间
        void move( float timeStep );

        //移动圆点并检查与瓷砖的碰撞
        // void move( Tile *tiles[] );

        //Centers the camera over the dot
        void setCamera( SDL_Rect& camera );

        //Shows the dot on the screen
        void render( SDL_Rect& camera );

        //在屏幕上渲染点
        void render();

        // 渲染屏幕上相对于当前框的点
        // 点必须相对于当前框进行渲染，因此它的渲染函数接收了当前框的位置
        void render( int camX, int camY );

        //获取每个点的碰撞方框组
        std::vector<SDL_Rect>& getColliders();

         //Gets collision circle
        Circle& getCollider();

        //Position accessors
        int getPosX();
        int getPosY();

    private:
        //The particles
        // Particle* particles[ TOTAL_PARTICLES ];

        //Shows the particles
        void renderParticles();

        //Collision box of the dot
        SDL_Rect mBox;

        //点的坐标
        // int mPosX, mPosY;
        float mPosX, mPosY;

        //点的速度
        // int mVelX, mVelY;
        float mVelX, mVelY;

        //点的碰撞方框组
        std::vector<SDL_Rect> mColliders;

        //Dot's collision circle
        Circle mCollider;

        //根据点的坐标移动碰撞框组
        void shiftColliders();
};


int Dot::getPosX()
{
    return mPosX;
}

int Dot::getPosY()
{
    return mPosY;
}

Dot::Dot(int x, int y)
{
    //Initialize the offsets
    mPosX = x;
    mPosY = y;

    //Set collision circle size
    mCollider.r = DOT_WIDTH / 2;

    //Initialize the velocity
    mVelX = 0;
    mVelY = 0;

    //Move collider relative to the circle
    shiftColliders();
}

void Dot::handleEvent( SDL_Event& e )
{
    //If a key was pressed
    // 如果每次按右键时都增加 x 位置，那么就必须反复按右键才能保持移动。而通过设置速度，只需按一次键即可
    //要检查按键重复次数是否为 0，因为按键重复次数默认是启用的，如果按住一个键，它会报告多次按键。这意味着必须检查按键是否是第一次按下，因为只关心按键第一次按下的时间
    if( e.type == SDL_KEYDOWN && e.key.repeat == 0 )
    {
        //Adjust the velocity
        switch( e.key.keysym.sym )
        {
            case SDLK_UP: mVelY -= DOT_VEL; break;
            case SDLK_DOWN: mVelY += DOT_VEL; break;
            case SDLK_LEFT: mVelX -= DOT_VEL; break;
            case SDLK_RIGHT: mVelX += DOT_VEL; break;
        }
    }
    //If a key was released
    else if( e.type == SDL_KEYUP && e.key.repeat == 0 )
    {
        //Adjust the velocity
        switch( e.key.keysym.sym )
        {
            case SDLK_UP: mVelY += DOT_VEL; break;
            case SDLK_DOWN: mVelY -= DOT_VEL; break;
            case SDLK_LEFT: mVelX += DOT_VEL; break;
            case SDLK_RIGHT: mVelX -= DOT_VEL; break;
        }
    }
}

Dot::Dot()
{
    //Initialize the collision box
    mBox.x = 0;
    mBox.y = 0;
	mBox.w = DOT_WIDTH;
	mBox.h = DOT_HEIGHT;

    //Initialize the velocity
    mVelX = 0;
    mVelY = 0;

    //Initialize particles
    // for( int i = 0; i < TOTAL_PARTICLES; ++i )
    // {
    //     particles[ i ] = new Particle( mPosX, mPosY );
    // }
}

Dot::~Dot()
{
    //Delete particles
    // for( int i = 0; i < TOTAL_PARTICLES; ++i )
    // {
    //     delete particles[ i ];
    // }
}

// 每帧移动圆点时调用的函数
void Dot::move(std::vector<SDL_Rect>& otherColliders)
{
    //Move the dot left or right
    mPosX += mVelX;
    shiftColliders();
    std::cout << "dot move" << ", mPosX=" << mPosX << std::endl;

    /*
    沿 x 轴移动圆点，同时也改变碰撞器的位置。每当改变点的位置时，碰撞器的位置也必须跟着改变。
    然后，检查点是否离开屏幕或撞到墙壁。如果有，就将点沿 x 轴向后移动。最后，再对 Y 轴上的运动做一次这样的检查。
    */
    //若点相撞或过于偏左或偏右
    if( ( mPosX < 0 ) || ( mPosX + DOT_WIDTH > SCREEN_WIDTH ) || checkCollision( this->mColliders, otherColliders ) )
    {
        //Move back
        mPosX -= mVelX;
        shiftColliders();
    }

    //Move the dot up or down
    mPosY += mVelY;
    shiftColliders();
    std::cout << "dot move" << ", mPosY=" << mPosY << std::endl;

    //若点相撞或过于偏上或偏下
    if( ( mPosY < 0 ) || ( mPosY + DOT_HEIGHT > SCREEN_HEIGHT ) || checkCollision( this->mColliders, otherColliders )  )
    {
        //Move back
        mPosY -= mVelY;
        shiftColliders();
    }
}

void Dot::move( SDL_Rect& square, Circle& circle )
{
    //Move the dot left or right
    mPosX += mVelX;
    shiftColliders();
    std::cout << "dot move" << ", mPosX=" << mPosX << ",mVelX=" << mVelX << std::endl;
    bool temp1 = checkCollision( mCollider, square );
    bool temp2 = checkCollision( mCollider, circle );
    std::cout << "dot move" << ", temp1=" << temp1 << ",temp2=" << temp2 << std::endl;

    //If the dot collided or went too far to the left or right
    if( ( mPosX - mCollider.r < 0 ) || ( mPosX + mCollider.r > SCREEN_WIDTH ) || checkCollision( mCollider, square ) || checkCollision( mCollider, circle ) )
    {
        //Move back
        mPosX -= mVelX;
        shiftColliders();
    }

    //Move the dot up or down
    mPosY += mVelY;
    shiftColliders();
    std::cout << "dot move" << ", mPosY=" << mPosY << ",mVelY=" << mVelY << std::endl;

    //If the dot collided or went too far up or down
    if( ( mPosY - mCollider.r < 0 ) || ( mPosY + mCollider.r > SCREEN_HEIGHT ) || checkCollision( mCollider, square ) || checkCollision( mCollider, circle ) )
    {
        //Move back
        mPosY -= mVelY;
        shiftColliders();
    }
}

void Dot::move()
{
    //Move the dot left or right
    mPosX += mVelX;
    std::cout << "dot move" << ", mPosX=" << mPosX << ",mVelX=" << mVelX << std::endl;

    // 要检查圆点是否移出了水平面，而不是检查圆点是否移出了屏幕，因为屏幕会在水平面周围移动
    //If the dot went too far to the left or right
    if( ( mPosX < 0 ) || ( mPosX + DOT_WIDTH > SCREEN_WIDTH) )
    {
        //Move back
        mPosX -= mVelX;
    }

    //Move the dot up or down
    mPosY += mVelY;
    std::cout << "dot move" << ", mPosY=" << mPosY << ",mVelY=" << mVelY << std::endl;

    //If the dot went too far up or down
    if( ( mPosY < 0 ) || ( mPosY + DOT_HEIGHT > SCREEN_HEIGHT ) )
    {
        //Move back
        mPosY -= mVelY;
    }
}

void Dot::move( float timeStep )
{
    //Move the dot left or right
    mPosX += mVelX * timeStep;

    //If the dot went too far to the left or right
    if( mPosX < 0 )
    {
        mPosX = 0;
    }
    else if( mPosX > SCREEN_WIDTH - DOT_WIDTH )
    {
        mPosX = SCREEN_WIDTH - DOT_WIDTH;
    }
    
    //Move the dot up or down
    mPosY += mVelY * timeStep;

    //If the dot went too far up or down
    if( mPosY < 0 )
    {
        mPosY = 0;
    }
    else if( mPosY > SCREEN_HEIGHT - DOT_HEIGHT )
    {
        mPosY = SCREEN_HEIGHT - DOT_HEIGHT;
    }
}

void Dot::shiftColliders()
{
    //行偏移量，每行碰状况高度不一致，所以需要变量记录当前行碰状况的y值
    // int r = 0;

    //查看点的碰撞框组
    // for( int set = 0; set < mColliders.size(); ++set )
    // {
    //     //将碰撞框居中
    //     mColliders[ set ].x = mPosX + ( DOT_WIDTH - mColliders[ set ].w ) / 2;

    //     //根据行偏移量设置碰撞框
    //     mColliders[ set ].y = mPosY + r;

    //     //将行偏移量向下移动碰撞框的高度
    //     r += mColliders[ set ].h;
    // }
    this->mCollider.x = this->mPosX;
    this->mCollider.y = this->mPosY;
}

std::vector<SDL_Rect>& Dot::getColliders()
{
    return mColliders;
}

Circle& Dot::getCollider()
{
    return mCollider;
}

void Dot::render()
{
     //Show the dot
    // gDotTexture.render( mPosX, mPosY );
    gDotTexture.render( (int)mPosX, (int)mPosY );

    //Show particles on top of dot
    // renderParticles();
}

// 将对象渲染到屏幕上时
void Dot::render( int camX, int camY )
{
    //Show the dot relative to the camera
    gDotTexture.render( mPosX - camX, mPosY - camY );
}

void Dot::renderParticles()
{
    //Go through particles
    // for( int i = 0; i < TOTAL_PARTICLES; ++i )
    // {
    //     //粒子渲染函数会检查是否有已死亡的粒子，并替换它们
    //     if( particles[ i ]->isDead() )
    //     {
    //         delete particles[ i ]; // 堆上的对象，用delete回收
    //         particles[ i ] = new Particle( mPosX, mPosY );
    //     }
    // }

    // //替换掉死粒子后，将所有当前粒子渲染到屏幕上
    // for( int i = 0; i < TOTAL_PARTICLES; ++i )
    // {
    //     particles[ i ]->render();
    // }
}

// 当移动圆点时，检查它是否偏离水平线或碰到墙砖
/*
void Dot::move( Tile *tiles[] )
{
    //Move the dot left or right
    mBox.x += mVelX;

    //If the dot went too far to the left or right or touched a wall
    if( ( mBox.x < 0 ) || ( mBox.x + DOT_WIDTH > LEVEL_WIDTH ) || touchesWall( mBox, tiles ) )
    {
        //move back
        // mBox.x -= mVelX;
        mVelX = -mVelX;
    }

    //Move the dot up or down
    mBox.y += mVelY;

    //If the dot went too far up or down or touched a wall
    if( ( mBox.y < 0 ) || ( mBox.y + DOT_HEIGHT > LEVEL_HEIGHT ) || touchesWall( mBox, tiles ) )
    {
        //move back
        // mBox.y -= mVelY;
        mVelY = -mVelY;
    }
}*/

void Dot::setCamera( SDL_Rect& camera )
{
    //Center the camera over the dot
    camera.x = ( mBox.x + DOT_WIDTH / 2 ) - SCREEN_WIDTH / 2;
    camera.y = ( mBox.y + DOT_HEIGHT / 2 ) - SCREEN_HEIGHT / 2;

    //Keep the camera in bounds
    if( camera.x < 0 )
    { 
        camera.x = 0;
    }
    if( camera.y < 0 )
    {
        camera.y = 0;
    }
    if( camera.x > LEVEL_WIDTH - camera.w )
    {
        camera.x = LEVEL_WIDTH - camera.w;
    }
    if( camera.y > LEVEL_HEIGHT - camera.h )
    {
        camera.y = LEVEL_HEIGHT - camera.h;
    }
}

void Dot::render( SDL_Rect& camera )
{
    //Show the dot
    gDotTexture.render( mBox.x - camera.x, mBox.y - camera.y );
}

bool init()
{
     //Initialization flag
    bool success = true;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        std::cout << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
        success = false;
    }
    else
    {
        //Set texture filtering to linear
        if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
        {
            std::cout << "Warning: Linear texture filtering not enabled!" << std::endl;
        }
        //Create window
        gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
        if( gWindow == nullptr )
        {
            std:: cout << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
            success = false;
        }
        else
        {
            //Create renderer for window
            // gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
            //为窗口创建垂直同步渲染器
            /*
            垂直同步,VSync 可以让渲染与显示器在垂直刷新时的更新同步进行。在本教程中，它将确保动画的运行速度不会太快。
            大多数显示器的运行速度约为每秒 60 帧，这也是我们的假设。如果你的显示器刷新率不同，这就能解释为什么动画运行得过快或过慢
            */
            gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
            if( gRenderer == nullptr )
            {
                std::cout << "Renderer could not be created! SDL Error:" << SDL_GetError() << std::endl;
                success = false;
            }
            else
            {
                //Initialize renderer color
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );

                //Initialize PNG loading
                int imgFlags = IMG_INIT_PNG;
                if( !( IMG_Init( imgFlags ) & imgFlags ) )
                {
                    std::cout << "SDL_image could not initialize! SDL_image Error:" << IMG_GetError() << std::endl;
                    success = false;
                }

                // //Initialize SDL_ttf
                // if( TTF_Init() == -1 )
                // {
                //     std::cout << "SDL_ttf could not initialize! SDL_ttf Error:" << TTF_GetError() << std::endl;
                //     success = false;
                // }
            }

        }
    }

    return success;
}

bool loadMedia()
{
    //Loading success flag
    bool success = true;

    //Load data stream
    if( !gDotTexture.loadFromFile("dot.bmp")  )
    {        
        std::cout << "Failed to load dot texture!" << std::endl;
        success = false;
    }
    // 像素加载完成后，对像素进行处理，使所有背景像素透明
    // else
    // {
    //     //Get pixel data
    //     //获取一个指向像素的指针，并获得要遍历的像素数
    //     Uint32* pixels = gFooTexture.getPixels32();
    //     int pixelCount = gFooTexture.getPitch32() * gFooTexture.getHeight();

    //     //Map colors
    //     //映射色键（本例中使用品红色）和透明色
    //     Uint32 colorKey = SDL_MapRGBA( SDL_GetWindowSurface( gWindow )->format, 0xFF, 0x00, 0xFF, 0xFF );
    //     Uint32 transparent = SDL_MapRGBA( SDL_GetWindowSurface( gWindow )->format, 0xFF, 0xFF, 0xFF, 0x00 );

    //     //Color key pixels
    //     for( int i = 0; i < pixelCount; ++i )
    //     {
    //         if( pixels[ i ] == colorKey )
    //         {
    //             // 找到所有关键色的像素，然后用透明像素替换它们
    //             pixels[ i ] = transparent;
    //         }
    //     }

    //     //Create texture from manually color keyed pixels
    //     if( !gFooTexture.loadFromPixels() )
    //     {
    //         std::cout << "Unable to load Foo' texture from surface!" << std::endl;
    //     }
    // }

    return success;
}

void close()
{
    //Destroy windows
    SDL_DestroyWindow(gWindow);
    gWindow = nullptr;
    SDL_DestroyRenderer(gRenderer);
    gRenderer = nullptr;

    gTargetTexture.free();
    gDotTexture.free();

    //Quit SDL subsystems
    // TTF_Quit();
    // Mix_Quit();
    IMG_Quit();
    SDL_Quit();
}

int main( int argc, char* args[] )
{
    //Start up SDL and create window
    if( !init() )
    {
        std::cout << "Failed to initialize!" << std::endl;
    }
    else
    {
        //Load media
        if( !loadMedia() )
        {
            std::cout << "Failed to load media!" << std::endl;
        }
        else
        {
            //Hack to get window to stay up
            SDL_Event e; 
            bool quit = false; 

            //The dot that will be moving around on the screen
            Dot dot;

            //Keeps track of time between steps
            LTimer stepTimer;

            //While application is running
            while( !quit )
            {
                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                    //Handle input for the dot
                    dot.handleEvent( e );
                }
                //Calculate time step
                float timeStep = stepTimer.getTicks() / 1000.f;  // 获取的是从上次定时器启动到当前的时间即一次循环时间

                std::cout << "timeStep=" << timeStep << std::endl;

                //Move for time step  计算点运行的到此时的位置，时间除1000是因为定时器返回的时间是ms，需转化为s
                dot.move( timeStep );   // 每次循环点都需要移动，但是移动的距离由速度和循环一次的时间决定，跟以前点的位置直接加速度不同

                //Restart step timer   重置定时器的启动时间
                stepTimer.start();  

                //Clear screen
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
                SDL_RenderClear( gRenderer );

                //Render dot
                dot.render();

                //Update screen
                SDL_RenderPresent( gRenderer );

            }
        }
        //Free resources and close SDL
        close();
    }

    return 0;
}



